package org.pupil.rpc.protocol.serialization.serializationImpl;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.pupil.rpc.protocol.RpcRequest;
import org.pupil.rpc.protocol.RpcResponse;
import org.pupil.rpc.protocol.serialization.RpcSerialization;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;

/**
 * JSON序列化
 */
public class JsonSerialization implements RpcSerialization {
    /**
     * ObjectMapper 是 Jackson 库中的一个核心类，
     * 用于序列化（将 Java 对象转换为 JSON）和反序列化（将 JSON 转换为 Java 对象）操作。
     */
    private static final ObjectMapper MAPPER;

    static {
        // 设置序列化策略，任何情况下都序列化该字段
        MAPPER = generateMapper(JsonInclude.Include.ALWAYS);
    }

    private static ObjectMapper generateMapper(JsonInclude.Include include) {
        ObjectMapper customMapper = new ObjectMapper();
        // 设置序列化包含级别
        customMapper.setSerializationInclusion(include);
        // 配置反序列化特性，禁用了未知属性的处理，当 JSON 中包含 Java 对象中没有的属性时，不会抛出异常
        customMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 配置反序列化特性，当枚举类型的字段在 JSON 中使用数字而不是字符串表示时，会抛出异常
        customMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
        // 设置日期格式化器，用于将 Java 中的日期对象格式化为特定的日期字符串
        customMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 返回对象
        return customMapper;
    }

    /**
     * 序列化
     *
     * @param obj
     * @param <T>
     * @return
     * @throws IOException
     */
    @Override
    public <T> byte[] serialize(T obj) throws IOException {
        // 判断传入参数是否为Sting，是就获得字符串的byte数组，不是用json序列化器，序列化参数为json字符串再通过UTF-8格式获得byte数组
        return obj instanceof String ? ((String) obj).getBytes() : MAPPER.writeValueAsString(obj).getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 反序列化
     * 解决jackson在反序列化对象时为LinkedHashMap的问题
     *
     * @param data
     * @param clazz
     * @param <T>
     * @return
     * @throws IOException
     */
    @Override
    public <T> T deSerialize(byte[] data, Class<T> clazz) throws IOException {
        // 将 JSON 数据转换成 Java 对象
        final T t = MAPPER.readValue(data, clazz);
        // 判断数据类型
        if (clazz.equals(RpcRequest.class)) {
            RpcRequest request = (RpcRequest) t;
            request.setData(convertBody(request.getData(), request.getDataClass()));
            return (T) request;
        } else {
            RpcResponse response = (RpcResponse) t;
            response.setData(convertBody(response.getData(), response.getDataClass()));
            return (T) response;
        }
    }

    /**
     * 处理消息体的数据
     *
     * @param data
     * @param clazz
     * @return
     */
    public Object convertBody(Object data, Class clazz) {
        // json字符串是否为空
        if (ObjectUtils.isEmpty(data)) {
            return null;
        }

        final Object o;
        // json字符串转换集合，并获得第一元素
        if (data instanceof String) {
            o = Arrays.asList(data).get(0);
        } else {
            o = ((ArrayList) data).get(0);
        }

        // 简单属性通常指的是基本数据类型及其包装类、字符串和日期等不需要特殊处理的属性
        // 判断是否为简单属性
        if (BeanUtils.isSimpleProperty(o.getClass())) {
            // 如果是简单属性的话集合里的值只有一个，所以直接返回o
            return o;
        }

        // 如果不是简单属性，转换为LinkedHashMap，调用convert方法将对象转换为指定类型的对象
        final LinkedHashMap map = (LinkedHashMap) o;
        return convertOther(clazz, map);
    }

    /**
     * 处理额外的数据
     *
     * @param clazz
     * @param map
     * @return
     */
    public Object convertOther(Class clazz, LinkedHashMap map) {
        // 获得额外处理对象字节对象
        final Class dataClass = clazz;

        try {
            // 通过反射获得对象
            Object o = dataClass.newInstance();
            // 遍历处理每个属性
            map.forEach((k, v) -> {
                try {
                    final Field field = dataClass.getDeclaredField(String.valueOf(k));
                    if (v != null && v.getClass().equals(LinkedHashMap.class)) {
                        // 如果v还是LinkedHashMap进行递归
                        v = convertOther(field.getType(), (LinkedHashMap) v);
                    }
                    // 设置字段可访问，以便设置属性值
                    field.setAccessible(true);
                    // 设置目标类实例的属性
                    field.set(o, v);
                    // 关闭字段可访问
                    field.setAccessible(false);
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            });
            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }
}
